/*
 * Elevator.h
 *
 *  Created on: 22/04/2012
 *      Author: alex
 */

#ifndef ELEVATOR_H_
#define ELEVATOR_H_

#include "FloorProxy.h"
#include "ElevatorComunicator.h"
#include "ApplicationCommon.h"
#include "ElevatorPassenger.h"
#include "Floor.h"
#include "../common/Object.h"
#include <cstdlib>

/**
 *
 */
class Elevator : public Object
{
private:
	/**
	 *
	 */
	ElevatorStatusEnum m_status;

	/**
	 * La distancia entre pisos esta medida en metros
	 */
	static const float FLOOR_DISTANCE = 3;

	/**
	 * Capacidad del ascensor en kg
	 */
    int m_averageSpeed;

    /**
     *
     */
    float m_maxWeight;

    /**
     *
     */
    ElevatorPassengerList m_passengers;

    /**
     *
     */
    FloorIdentifierList m_hallCalls;
    
    /**
     *
     */
    FloorIdentifier m_currentFloor;
    
    FloorProxy m_floorProxy;

    ElevatorComunicator m_destinationCommunicator;

    bool m_keepAceptingPassengers;
    bool m_doRun;
    bool m_elevatorCalled;

	/**
	 *
	 */
	ElevatorStatusEnum getNextStatus();

	/**
	 *
	 */
	ElevatorStatusEnum getNextStatusFrom( FloorIdentifierList destinations );

	/**
	 *
	 */
	float getCurrentWeight();

	/**
	 * Devuelve la lista de llamadas de cabina (Destinos de los pasajeros en el ascensor)
	 */
	FloorIdentifierList getCarCalls();

	/**
	 * Devuelve la lista de llamadas de palier (hall calls)
	 */
	FloorIdentifierList& getHallCalls();

	/**
	 * Devuelve la lista COMPLETA de llamadas, incluye llamas de palier (hall calls) y llamadas de cabina (car calls)
	 */
	FloorIdentifierList getAllCalls();

	/**
	 * Agrega un destino al ascensor
	 * @param destination destino a agregar
	 */
	void addHallCall(FloorIdentifier destination);

	/**
	 * Marca la el pedido de palier para el piso dado como atendido. Elimina la llamada de palier de la lista de llamadas almacenada en elascensor.
	 */
	void markHallCallAsServed(FloorIdentifier destination);

public:

    /**
     * Constructor
     * @param averageSpeed Velocidad promedio del ascensor
     * @param maxCapacity capacidad max del ascensor
     * @param initialFloor Piso del cual arranca el ascensor
     */
	Elevator(float averageSpeed, float maxCapacity, FloorIdentifier initialFloor,int floorNums,std::string baseFileName, int elevatorNumber, std::string keyGeneratorPath);

	/**
	 * Destructor
	 */
	virtual ~Elevator();

	/**
	 * Obtiene la capacidad maxima del ascensor
	 * @returns float capacidad
	 */
	float getMaxCapacity();

	/**
	 * Devuelve el peso que queda para usar en el ascensor
	 * (capacidad)
	 * @returns float capacidad actual
	 */
	float getCurrentCapacity();

	/**
	 * Carga un pasajero en el ascensor
	 * Si el ascensor no tiene capacidad restante avisa que no puede
	 * cargar el pasajero.
	 * @param passenger
	 * @returns bool true si pudo cargar al pasajero exitosamente, false
	 *          en caso contrario
	 */
	bool loadPassenger(ElevatorPassenger &passenger);

	/**
	 * Descarga todos los pasajeros que marcaron como destino el piso
	 * en el que me encuentro
	 * @returns ElevatorPassengerList lista de pasajeros que se bajaron en el piso.
	 */
	ElevatorPassengerList unloadPassengers();

	/**
	 * Agrego una lista de pisos a los cuales en los cuales tiene que parar el
	 * ascensor.
	 * @param destinations lista de destinos
	 */
	// ToDo: Transformar en setHallRequests, y eliminar la llamada de esto desde addPassenger()
	void addFloorRequests(FloorIdentifierList &destinations);

	/**
	 * Agrega un piso en el cual se debe parar
	 * @param destination id del piso que debe parar
	 */
	// ToDo: Transformar en addHallRequest, y eliminar la llamada de esto desde addPassenger()
	void addFloorRequest(FloorIdentifier destination);

	/**
	 * Obtiene el status actual del ascensor.
	 * @returns ElevatorStatusEnum status actual.
	 */
	ElevatorStatusEnum getStatus();

	/**
	 * Obtiene el id del piso en el cual se encuentra el ascensor
	 * actualmente.
	 * @returns FloorIdentifier id del piso actual
	 */
	FloorIdentifier getCurrentFloor();

	/**
	 * Retorna si hay un request para un piso determinado
	 * @param floor id del piso en el cual se pregunta si debe parar.
	 * @returns bool true si debe parar, false en caso contrario.
	 */
	bool hasRequestInFloor(FloorIdentifier floor);

	/**
	 * Retorna si debe parar en el piso actual.
	 * @returns bool true si debe parar, false en caso contrario.
	 */
	bool willStopAtCurrentFloor();

	/**
	 * Simula el movimiento de un ascensor, moviendose de un piso
	 * por cada llamada, en una direccion determinada por la logica
	 * de funcionamiento del ascensor y los request que tiene.
	 */
	void handleRequests();

	/**
	 * Obtiene el numero de pasajeros del ascensor
	 * @returns int numero de pasajeros
	 */
	int getPassengerCount();

	/**
	 * Devuelve si hay pasajeros que descargar en un piso determinado.
	 * @param floor id del piso en el cual se pregunta si bajaran pasajeros.
	 * @returns bool true si hay pasajeros para descargar, false en caso contrario.
	 */
	bool hasPassengersToUnloadAt(FloorIdentifier floor);

	/**
	 * Para el ciclo principal de la aplicacion
	 */
	void stopSimulation();

	/**
	 * Comienza el ciclo principal de la simulacion de un piso
	 * Genera pasajeros en un tiempo aleatorio y los envia a traves
	 * del PassengerQueue
	 */
	void startSimulation();
};

#endif /* ELEVATOR_H_ */
